﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CsvHelperPlus;
using System.Data;
using System.IO;
using System.Collections.Concurrent;

namespace DataSource
{
    public class SimulateMarket
    {
        static private SimulateMarket sm = null;
        string date;  // 哪一天
        private string stkPath; // 股票列表文件
        private List<string> stkList;
        private List<TickStruct> tickList;
        private List<Level2Struct> level2List;
        private ConcurrentDictionary<string, Level2Struct> realTimeData;
        public delegate void TickReceiveEventHandler(TickStruct ts);
        public delegate void Level2ReceiveEventHandler(Level2Struct ls);
        public event TickReceiveEventHandler OnTickReceiveEvent;
        public event Level2ReceiveEventHandler OnLevel2ReceiveEvent;
        ILoadData ldd;
        
        private int timePos;  // 记录行情播放的位置
        public SimulateMarket(string date)
        {
            stkPath = @"P:\public\sqli\sharedata\univ2.1.4";
            this.date = date;
            //stkList = GetAllMarketStockList(date);
            //ldd = new LoadCsvFromDFW(date);
            ldd = new LoadMarketData(date);
            timePos = 0;
            realTimeData = new ConcurrentDictionary<string, Level2Struct>();
        }

        

        static public SimulateMarket GetInstance(string date = null)
        {
            if(date == null)
            {
                if(sm == null)
                {
                    throw new Exception("SimulateMarket has not init");
                }
                return sm;
            }
            else
            {
                if(sm == null)
                {
                    sm = new SimulateMarket(date);
                    return sm;
                }
                else if (sm.date != date) //如果相同的单日循环测试参数，可能会出错
                {
                    sm = new SimulateMarket(date);
                    return sm;
                }
                else
                {
                    return sm;
                }
            }
        }


        static public void ClearInstance() //每次循环完，需要重新clear
        {
            if (sm != null)
            {
                sm = null;
            }
        } 


        public List<string> GetAllMarketStockList()
        {
            DataTable dt = GetUniv(date);
            List<string> stkList = dt.AsEnumerable().Where(x=>x["DELISTDATE"]=="99999999").Select(x => x["#TICKER"].ToString().Substring(0, 6)).ToList();
            return stkList;
        }

        public DataTable GetUniv(string date, Dictionary<string, Type> convert=null)
        {
            string stkListFile = stkPath + "\\" + date + ".univ.csv";
            DataTable dt = CsvHandler.ReadCsvToDataTable(stkListFile, convert:convert);
            var rows = dt.AsEnumerable().Where(x => !x["#TICKER"].ToString().EndsWith(".SS")); // 去掉指数
            rows = rows.Where(x => x["DELISTDATE"].ToString() == "99999999" && x["ISST"].ToString()=="0");  //去掉退市和ST
            DataTable dtNew = dt.Clone();
            foreach(DataRow r in rows){
                dtNew.ImportRow(r);
            }
            return dtNew;
        }

        public Dictionary<string, string> GetName()
        {
            DataTable dt = SimulateMarket.GetInstance().GetUniv(date);
            var nameMapList = dt.AsEnumerable().Select(x => new { code = x["#TICKER"].ToString().Substring(0, 6), name = x["NAME"].ToString() });
            Dictionary<string, string> nameDict = new Dictionary<string, string>();
            foreach (var nameMap in nameMapList)
            {
                nameDict[nameMap.code] = nameMap.name;
            }
            return nameDict;

        }

        public Level2Struct GetNextMarketData()
        {
            if (timePos < level2List.Count)
                return level2List[timePos++];
            else
                return null;
        }

        public void MoveBackMarketData()
        {
            timePos--;
        }

        public Level2Struct GetMarketSnapshot(string code) //获取最新市场行情
        {
            return realTimeData[code];
        }

        public bool IsMarketDataAvailable(string code)
        {
            return realTimeData.ContainsKey(code);
        }

        public TickStruct GetMarketTick(string code, DateTime dt)
        {
            TickStruct ts = new TickStruct();
            return ts;
        }


        public List<TickStruct> GetTickList(string code, DateTime sdt, DateTime edt)
        {
            List<TickStruct> lts = ldd.LoadTick(code);
            var res = from it in lts where (it.time >= sdt) && (it.time <= edt) orderby it.time ascending select it;
            return res.ToList();
        }

        public Level2Struct GetLv2(string code, DateTime dt) //获取信号发出后，最新的lv2快照
        {
            List<Level2Struct> lls = ldd.LoadLv2(code);
            var res = from it in lls where it.time > dt orderby it.time ascending select it;
            return res.First();
        }

        //public DataTable LoadLv2(string code)
        //{
        //    string se = "";
        //    if(code[0] == '6')
        //    {
        //        se = "sh";
        //    }
        //    else
        //    {
        //        se = "sz";
        //    }
        //    string filePath = lv2Path + "\\" + date + "\\" + se + "\\" + code + "_" + date + ".csv";
        //    DataTable lv2Table = CsvHandler.ReadCSV(filePath);                     
        //    lv2Table.Columns["证券代码"].ColumnName = "code";
        //    lv2Table.Columns["最新"].ColumnName = "price";
        //    lv2Table.Columns["成交笔数"].ColumnName = "count";
        //    lv2Table.Columns["成交量"].ColumnName = "amount";
        //    lv2Table.Columns["成交额"].ColumnName = "balance";
        //    string[] chgNum = { "一", "二", "三", "四", "五" };
        //    string[] engNum = { "1", "2", "3", "4", "5" };
        //    for (int i = 0; i < chgNum.Length; i++)
        //    {
        //        lv2Table.Columns["买" + chgNum[i] + "价"].ColumnName = "bid_px" + engNum[i];
        //        lv2Table.Columns["卖" + chgNum[i] + "价"].ColumnName = "ask_px" + engNum[i];
        //        lv2Table.Columns["买" + chgNum[i] + "量"].ColumnName = "bid_amt" + engNum[i];
        //        lv2Table.Columns["卖" + chgNum[i] + "量"].ColumnName = "ask_amt" + engNum[i];
        //    }
        //    lv2Table.Columns.Add("datetime", typeof(DateTime));
        //    for (int i=0; i < lv2Table.Rows.Count; i++)
        //    {
        //        string time = lv2Table.Rows[i]["时间"].ToString();
        //        lv2Table.Rows[i]["datetime"] = DateTime.ParseExact(time, "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.CurrentCulture);
        //    }
        //    return lv2Table;
        //}

        //public Level2Struct BuildLevel2Struct(DataRow r)
        //{
        //    Level2Struct ls = new Level2Struct();
        //    ls.amount = double.Parse(r["amount"].ToString());
        //    ls.balance = double.Parse(r["balance"].ToString());
        //    ls.count = int.Parse(r["count"].ToString());
        //    ls.time = (DateTime)r["datetime"];
        //    ls.price = float.Parse(r["price"].ToString());
        //    ls.code = r["code"].ToString();
        //    for(int i=1; i<6; i++)
        //    {
        //        ls.buy_px.Add(float.Parse(r["bid_px" + i.ToString()].ToString()));
        //        ls.buy_amount.Add(int.Parse(r["bid_amt" + i.ToString()].ToString()));
        //        ls.sell_px.Add(float.Parse(r["ask_px" + i.ToString()].ToString()));
        //        ls.sell_amount.Add(int.Parse(r["ask_amt" + i.ToString()].ToString()));
        //    }
        //    return ls;
        //}


        //private DataTable LoadTick(string code)
        //{
        //    string prefix = "";
        //    if(code[0] == '6')
        //    {
        //        prefix = "SH";
        //    }
        //    else
        //    {
        //        prefix = "SZ";
        //    }
        //    string filePath = tickPath + "\\" + date + "\\" + prefix + code + ".csv";
        //    DataTable data = CsvHandler.ReadCSV(filePath);
        //    // 增加code列
        //    DataColumn codeColumn = new DataColumn();
        //    codeColumn.DataType = typeof(string);
        //    codeColumn.ColumnName = "code";
        //    codeColumn.DefaultValue = code;
        //    data.Columns.Add(codeColumn);
        //    return data;
        //}

        public void Subscrible(List<string> tickerList, DType type) //订阅行情, Dtype代表行情类型
        {
            stkList = tickerList;
            switch (type)
            {
                case DType.LV2:
                    Level2Market();
                    break;
                case DType.Tick:
                    TickMarket();
                    break;
                default:
                    throw new Exception("no such market data");
            }
        }


        public void OnTimePulse(DateTime dt) //响应时间信号，推送行情
        {
            Level2Struct ls = GetNextMarketData();
            if (ls == null) return;
            while(ls.time <= dt)
            {
                OnLevel2ReceiveEvent(ls);
                realTimeData[ls.code] = ls;
                ls = GetNextMarketData(); // 获取下一个行情快照
                if (ls == null) break;
            }
            MoveBackMarketData(); //退回一格行情
        }


        public List<DateTime> GetTimeList()
        {
            List<DateTime> timeList = level2List.Select(x => x.time).Distinct().ToList();
            return timeList.OrderBy(x=>x).ToList();
        }

        //private DateTime ToDateTime(string time)
        //{
        //    DateTime dt = DateTime.ParseExact(time, "yyyyMMddHHmmssfff", System.Globalization.CultureInfo.CurrentCulture);
        //    return dt;
        //}

        private void TickMarket() // Tick行情源
        {
            tickList = ldd.LoadMarketTick(stkList);
        }

        private void Level2Market() // Level2行情源
        {
            level2List = ldd.LoadMarketLv2(stkList);
        }


        //private TickStruct BuildTickStruct(DataRow r)
        //{
        //    TickStruct ts = new TickStruct();
        //    ts.code = r["code"].ToString();
        //    ts.time = (DateTime)r["datetime"];
        //    ts.price = float.Parse(r["Price"].ToString());
        //    ts.volume = int.Parse(r["Volume"].ToString());
        //    ts.direction = r["Direction"].ToString();
        //    return ts;
        //}
    }
}
